<html>
  <head>
    <style>
       :visited:before, :visited *:before {
          content: "visited";
          color: red;
          border: lime 10px solid;
          fill: red;
          stroke: red;
      }
    </style>
  </head>
  <body onload="init()">
    <script>
      if (window.testRunner) {
          testRunner.keepWebHistory();
          testRunner.dumpAsText();
          testRunner.waitUntilDone();
      }
      var oVisitedLink, oUnvisitedLink;
      function init() {
        oVisitedLink = createInvisibleLink(location.href);
        oUnvisitedLink = createInvisibleLink('http://example.com/' + Math.random());
        setTimeout(function () {
          asDifferences = compareStyles(oUnvisitedLink, oVisitedLink);
          var sDifferent = asDifferences.length == 0 ?
              'No differences noted.' :
              'Differences noted.';
          document.body.appendChild(document.createElement('strong')).appendChild(document.createTextNode(sDifferent));
          oVisitedLink.style.display = 'none';
          oUnvisitedLink.style.display = 'none';
          if (window.testRunner)
              testRunner.notifyDone();
        }, 0);
      }
      function createInvisibleLink(sUrl) {
        var oDiv = document.body.appendChild(document.createElement("div"));
        var oLink = oDiv.appendChild(document.createElement('a'));
        oLink.href = sUrl;
        oLink.appendChild(document.createTextNode(sUrl));
        oLink.onclick = function () { return false;}
        return oLink
      }
      function readStyleProperties(oStyleDeclaration) {
        var dxStyleProperties = {};
        for (var i = 0; i < oStyleDeclaration.length; i++) {
          var sPropertyName = oStyleDeclaration.item(i);
          var dxPropertyAttributes = {
              'value': oStyleDeclaration.getPropertyValue(sPropertyName),
              'priority': oStyleDeclaration.getPropertyPriority(sPropertyName)
          };
          dxStyleProperties[sPropertyName] = dxPropertyAttributes;
        }
        return dxStyleProperties;
      }
      function compareStyles(oLink1, oLink2) {
        var dxStyleProperties1 = readStyleProperties(getComputedStyle(oLink1, "::before"));
        var dxStyleProperties2 = readStyleProperties(getComputedStyle(oLink2, "::before"));
        var asExtraProperties = [], asDifferentProperties = [], asMissingProperties = [];
        for (sProperty in dxStyleProperties1) {
          if (!(sProperty in dxStyleProperties2)) {
            asExtraProperties.push(sProperty);
          } else {
            var dxPropertyAttributes1 = dxStyleProperties1[sProperty];
            var dxPropertyAttributes2 = dxStyleProperties2[sProperty];
            for (sAttribute in dxPropertyAttributes1) {
              if (dxPropertyAttributes1[sAttribute] != dxPropertyAttributes2[sAttribute]) {
                asDifferentProperties.push(sProperty + ' (' + sAttribute + ')');
              }
            }
          }
        }
        for (sProperty in dxStyleProperties2) {
          if (!(sProperty in dxStyleProperties1)) {
            asMissingProperties.push(sProperty);
          }
        }
        asDifferences = [];
        if (asExtraProperties.length > 0) asDifferences.push('Extra properties: ' + asExtraProperties.join(', ') + '.');
        if (asMissingProperties.length > 0) asDifferences.push('Missing properties: ' + asMissingProperties.join(', ') + '.');
        if (asDifferentProperties.length > 0) asDifferences.push('Different properties: ' + asDifferentProperties.join(', ') + '.');
        return asDifferences;
      }
    </script>
  </body>
</html>
